#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>
#include <stdlib.h>

#define CAPACITY 10 
#define COUNT 10

int Data = 0;

class RingQueue{
  public:
    RingQueue(int Capacity_ = CAPACITY)
    :Capacity(Capacity_)
    ,rq(Capacity_)
    ,pro_pos(0)
    ,com_pos(0){
        sem_init(&pro_sem, 0, Capacity);
        sem_init(&con_sem, 0, 0);
        sem_init(&sem_mutex, 0, 1);
    }
    ~RingQueue(){
      sem_destroy(&pro_sem);
      sem_destroy(&con_sem);
    }
    void Push(){
      sem_wait(&sem_mutex);
      sem_wait(&pro_sem);
      ++Data;
      rq[pro_pos] = Data;
      ++pro_pos;
      pro_pos %= Capacity;
      sem_post(&con_sem);
      sem_post(&sem_mutex);
    }
    void Pop(int& Data){
      sem_wait(&sem_mutex);
      sem_wait(&con_sem);
      Data = rq[com_pos];
      ++com_pos;
      com_pos %= Capacity;
      sem_post(&pro_sem);
      sem_post(&sem_mutex);
    }
  private:
    int Capacity;
    std::vector<int> rq;
    int pro_pos;
    int com_pos;
    sem_t pro_sem;
    sem_t con_sem;
    sem_t sem_mutex;
};


void* produce(void* arg){
  RingQueue* rq = (RingQueue*)arg;
  for(int i = 0; i < 3; ++i){
    rq->Push();
    std::cout << "Produce Data: " << Data << std::endl;
  }
  return NULL;
}

void* consume(void* arg){
  RingQueue* rq = (RingQueue*)arg;
  for(int i = 0; i < 3; ++i){
    int Data;
    rq->Pop(Data);
    std::cout << "Consume Data: " << Data << std::endl;
  }
  return NULL;
}

int main(){
  RingQueue rq;
  srand(time(NULL));
  pthread_t con_tid[COUNT], pro_tid[COUNT];
  for(int i = 0; i < COUNT; ++i){
    pthread_create(&con_tid[i], NULL, consume, &rq);  
    pthread_create(&pro_tid[i], NULL, produce, &rq);
  }
  for(int i = 0; i < COUNT; ++i){
    pthread_join(con_tid[i], NULL);
    pthread_join(pro_tid[i], NULL);
  }
  return 0;
}
